﻿#region Copyright (c) 2000-2008 Developer Express Inc.
/*
{*******************************************************************}
{                                                                   }
{       Developer Express .NET Component Library                    }
{       AgDataGrid                                 }
{                                                                   }
{       Copyright (c) 2000-2008 Developer Express Inc.              }
{       ALL RIGHTS RESERVED                                         }
{                                                                   }
{   The entire contents of this file is protected by U.S. and       }
{   International Copyright Laws. Unauthorized reproduction,        }
{   reverse-engineering, and distribution of all or any portion of  }
{   the code contained in this file is strictly prohibited and may  }
{   result in severe civil and criminal penalties and will be       }
{   prosecuted to the maximum extent possible under the law.        }
{                                                                   }
{   RESTRICTIONS                                                    }
{                                                                   }
{   THIS SOURCE CODE AND ALL RESULTING INTERMEDIATE FILES           }
{   ARE CONFIDENTIAL AND PROPRIETARY TRADE                          }
{   SECRETS OF DEVELOPER EXPRESS INC. THE REGISTERED DEVELOPER IS   }
{   LICENSED TO DISTRIBUTE THE PRODUCT AND ALL ACCOMPANYING .NET    }
{   CONTROLS AS PART OF AN EXECUTABLE PROGRAM ONLY.                 }
{                                                                   }
{   THE SOURCE CODE CONTAINED WITHIN THIS FILE AND ALL RELATED      }
{   FILES OR ANY PORTION OF ITS CONTENTS SHALL AT NO TIME BE        }
{   COPIED, TRANSFERRED, SOLD, DISTRIBUTED, OR OTHERWISE MADE       }
{   AVAILABLE TO OTHER INDIVIDUALS WITHOUT EXPRESS WRITTEN CONSENT  }
{   AND PERMISSION FROM DEVELOPER EXPRESS INC.                      }
{                                                                   }
{   CONSULT THE END USER LICENSE AGREEMENT FOR INFORMATION ON       }
{   ADDITIONAL RESTRICTIONS.                                        }
{                                                                   }
{*******************************************************************}
*/
#endregion Copyright (c) 2000-2008 Developer Express Inc.

using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;
using System;
using System.Threading;
using System.Windows.Threading;
using System.Windows.Controls.Primitives;
namespace DevExpress.AgDataGrid.Internal {
	public interface IDragableObject {
		FrameworkElement CreateDragShape();
	}
	public interface IDropableObject {
		bool CanAccept();
		bool CanAccept(IDragableObject dragObject, Point position);
		Rect GetThumbRect(Point position);
		void AcceptDrag(IDragableObject dragObject, Point position);
		FrameworkElement GetThumbObject();
	}
	public interface IScrollOnDragging {
		bool IsInScrollArea(Point position);
		void DoScroll();
	}
	public class DragController {
		public const double DefaultStartDragShift = 3;
		public const double ScrollingTimerDelay = 100;
		bool isDragging;
		IDragableObject dragableObject;
		FrameworkElement dragShape;
		IDropableObject currentDropableObject;
		FrameworkElement currentThumb;
		Point currentMoveTo;
		bool isInScrollableArea;
		DispatcherTimer scrollTimer;
		Canvas surfaceCanvas;
		Point localShift;
		Point localMargin;
		public DragController(IDragableObject dragableObject, Canvas surfaceCanvas, Point startPt, Point margin) {
			this.dragableObject = dragableObject;
			this.surfaceCanvas = surfaceCanvas;
			this.localMargin = margin;
			this.dragShape = DragableObject.CreateDragShape();
			LocalShift = startPt;
			DragShape.Visibility = Visibility.Collapsed;
			SurfaceCanvas.Children.Add(DragShape as FrameworkElement);
			StartDragShift = DefaultStartDragShift;
		}
		public bool IsDragging { get { return isDragging; } }
		public double StartDragShift { get; set; }
		public IDragableObject DragableObject { get { return dragableObject; } }
		public IScrollOnDragging ScrollOnDragging { get; set; }
		public bool IsInScrollableArea { get { return isInScrollableArea; } }
		protected Point LocalShift { get { return localShift; } set { localShift = value; } }
		protected Point LocalMargin { get { return localMargin; } set { localMargin = value; } }
		protected Canvas SurfaceCanvas { get { return surfaceCanvas; } }
		protected internal FrameworkElement DragShape { get { return dragShape; } }
		protected IDropableObject CurrentDropableObject { get { return currentDropableObject; } }
		protected Point CurrentMoveTo { get { return currentMoveTo; } }
		protected internal FrameworkElement CurrentThumb { 
			get { return currentThumb; }
			set {
				if(value == CurrentThumb) return;
				if(CurrentThumb != null) {
					SurfaceCanvas.Children.Remove(CurrentThumb);
				}
				currentThumb = value;
				if(CurrentThumb != null) {
					SurfaceCanvas.Children.Add(CurrentThumb);					
				}
			}
		}
		protected Point CachedMoveTo { get; set; }
		public void Move(Point moveTo) {
			CachedMoveTo = moveTo;
			if(!IsDragging) {
				CheckCanDrag(moveTo);
				if(!IsDragging) return;
			}
			Move(moveTo, FindDropableObject(moveTo)); 
		}
		protected virtual IDropableObject FindDropableObject(Point point) {
			return FindDropableObject(point, SurfaceCanvas);
		}
		protected virtual IDropableObject FindDropableObject(Point point, FrameworkElement surface) {
			if(surface == null || surface == DragShape) return null;
			Point localPt = TransformFromSurfaceCanvas(point, surface);
			IDropableObject dropableObject = surface as IDropableObject;
			if(dropableObject != null && dropableObject.CanAccept() && IsIn(surface, localPt)) {
				return dropableObject;
			}
			Panel panel = surface as Panel;
			if(panel != null) {
				for(int i = panel.Children.Count - 1; i >= 0; i--) {
					IDropableObject found = FindDropableObject(point, panel.Children[i] as FrameworkElement);
					if(found != null) return found;
				}
			}
			return null;
		}
		bool IsIn(FrameworkElement element, Point localPt) {			
			return element != null && localPt.X > 0 && localPt.Y > 0 && localPt.X < element.ActualWidth && localPt.Y < element.ActualHeight;
		}
		protected Point TransformFromSurfaceCanvas(Point pt, object obj) {
			return TransformSurface(pt, obj, SurfaceCanvas);
		}
		protected virtual Point TransformSurface(Point pt, object from, object to) {
			UIElement fromElement = from as UIElement;
			UIElement toElement = to as UIElement;
			if(fromElement == null || toElement == null) return pt;
			GeneralTransform gt = toElement.TransformToVisual(fromElement);
			return gt.Transform(pt);
		}
		protected void Move(Point moveTo, IDropableObject drop) {
			this.currentDropableObject = drop;
			DragShape.Visibility = Visibility.Visible;
			this.currentMoveTo = TransformFromSurfaceCanvas(moveTo, drop);
			if(drop != null && drop.CanAccept(DragableObject, CurrentMoveTo)) {
				Rect rect = drop.GetThumbRect(CurrentMoveTo);
				ShowThumb(drop.GetThumbObject(), rect);
			} else {
				CurrentThumb = null;
			}
			dragShape.RenderTransform = new TranslateTransform { X = moveTo.X - LocalShift.X - LocalMargin.X, Y = moveTo.Y - LocalShift.Y - LocalMargin.Y };
			CheckScrollArea(moveTo);
		}
		protected virtual void CheckScrollArea(Point p) {
			SetIsInScrollableArea(ScrollOnDragging != null && ScrollOnDragging.IsInScrollArea(p));
		}
		protected void SetIsInScrollableArea(bool value) {
			if (value == IsInScrollableArea) return;
			this.isInScrollableArea = value;
			StopScrollingTimer();
			if (value) {
				StartScrollTimer();
			}
		}
		protected virtual void StartScrollTimer() {
			scrollTimer = new DispatcherTimer();
			scrollTimer.Interval = TimeSpan.FromMilliseconds(ScrollingTimerDelay);
			scrollTimer.Tick += new EventHandler(scrollTimer_Tick);
			scrollTimer.Start();
		}
		void scrollTimer_Tick(object sender, EventArgs e) {
			DoScroll();
		}
		protected virtual void DoScroll() {
			if (ScrollOnDragging != null && IsInScrollableArea) {
				ScrollOnDragging.DoScroll();
				Move(CachedMoveTo);
			}
		}
		void StopScrollingTimer() {
			if (scrollTimer == null) return;
			scrollTimer.Stop();
			scrollTimer = null;
		}
		protected void CheckCanDrag(Point moveTo) {
			if(Math.Abs(LocalShift.X - TransformFromSurfaceCanvas(moveTo, DragableObject).X) > DefaultStartDragShift ||
				Math.Abs(LocalShift.Y - TransformFromSurfaceCanvas(moveTo, DragableObject).Y) > DefaultStartDragShift) {
				this.isDragging = true;
			} 
		}
		public void StopDrag() {
			StopScrollingTimer();
			SurfaceCanvas.Children.Remove(DragShape as FrameworkElement);
			CurrentThumb = null;
			if(CurrentDropableObject != null && CurrentDropableObject.CanAccept(DragableObject, CurrentMoveTo)) {
				CurrentDropableObject.AcceptDrag(DragableObject, CurrentMoveTo);
			}
		}
		protected void ShowThumb(FrameworkElement thumb, Rect rect) {
			if(SurfaceCanvas == null || CurrentDropableObject == null) return;
			CurrentThumb = thumb;
			if(CurrentThumb != null) {
				if(rect.Width > 0) {
					CurrentThumb.Width = rect.Width;
				}
				if(rect.Height > 0) {
					CurrentThumb.Height = rect.Height;
				}
				Point pt = TransformSurface(new Point(rect.Left, rect.Top), SurfaceCanvas, CurrentDropableObject);
				CurrentThumb.SetValue(Canvas.LeftProperty, pt.X);
				CurrentThumb.SetValue(Canvas.TopProperty, pt.Y);
			}
		}
	}
}
